﻿/*!
 * @file drv_lpspi_edma.c
 * @author suyong_yq@126.com
 * @note
 * 本模块实现通过EDMA驱动LPSPI传输引擎收发数据
 */

#include "drv_lpspi_edma.h"

/* EDMA传输通道配置模板 */
#define DRV_LPSPI_EDMA_CONFIG_DEFAULT_STRUCT {              \
    .SrcAddr                     = 0U,                      \
    .DestAddr                    = 0U,                      \
    .MinorLoopByteCount          = 0U,                      \
    .SrcBusWidthMode             = eEDMA_BusWidth_1Byte,    \
    .DestBusWidthMode            = eEDMA_BusWidth_1Byte,    \
    .SrcAddrCycleMode            = eEDMA_AddrCycle_Disabled,\
    .DestAddrCycleMode           = eEDMA_AddrCycle_Disabled,\
    .SrcAddrIncPerTransfer       = 0,                       \
    .DestAddrIncPerTransfer      = 0,                       \
    .SrcAddrMinorLoopDoneOffset  = 0,                       \
    .DestAddrMinorLoopDoneOffset = 0,                       \
    .MinorLoopCount              = 0,                       \
    .SrcAddrMajorLoopDoneOffset  = 0,                       \
    .DestAddrMajorLoopDoneOffset = 0,                       \
}

/* LPSPI传输配置命令模板 */
#define DRV_LPSPI_XFER_COMMAND_DEFAULT_STRUCT {    \
    .PolPhaMode     = eLPSPI_PolPhaMode0,          \
    .ClkDiv         = eLPSPI_ClkDiv_64,            \
    .WhichCS        = 0U,                          \
    .enLSB          = false,                       \
    .enByteSwap     = false,                       \
    .enKeepCS       = false,                       \
    .DataBitsPerCmd = 8U                           \
}

/*!
 * @brief 初始化传输操作
 * @note  初始化硬件，关联结构体数据
 */
void LPSPI_EDMA_MasterInit(
    LPSPI_EDMA_MasterXferHandler_T *xferHandler,
    LPSPI_EDMA_HwDev_T       *hwDevPtr,
    LPSPI_MasterConfig_T     *lpspiConfigPtr
)
{
    /* 关联数据结构 */
    xferHandler->HwDevPtr = hwDevPtr;

    /* 初始化SPI为Master模式 */
    LPSPI_HAL_Reset(hwDevPtr->LpspiBase);
    LPSPI_HAL_Enable(hwDevPtr->LpspiBase, true);
    LPSPI_HAL_ConfigMaster(hwDevPtr->LpspiBase, lpspiConfigPtr);
    LPSPI_HAL_EnableTxDMA(hwDevPtr->LpspiBase, true);
    LPSPI_HAL_EnableRxDMA(hwDevPtr->LpspiBase, true);

    /* Reset Tx DMA channel. */
    DMAMUX_HAL_SetTriggerForDMA(hwDevPtr->EdmaLpspiTxChnIdx, eDMAMUX_TriggerSource_Disabled); /* 确保触发窗口是关闭的 */
    EDMA_HAL_ResetChannel(hwDevPtr->EdmaLpspiTxChnIdx);

    /* Reset Rx DMA channel. */
    DMAMUX_HAL_SetTriggerForDMA(hwDevPtr->EdmaLpspiRxChnIdx, eDMAMUX_TriggerSource_Disabled); /* 确保触发窗口是关闭的 */
    EDMA_HAL_ResetChannel(hwDevPtr->EdmaLpspiRxChnIdx);
}

/*!
 * @brief 启动传输
 * @note  启动SPI传输，传输完毕后由中断服务程序通知应用程序
 */
void LPSPI_EDMA_StartMasterXfer(
    LPSPI_EDMA_MasterXferHandler_T *xferHandler,
    uint8_t *txBufPtr, uint8_t *rxBufPtr, uint32_t xferLen,
    void (*xferDonecallback)(void)
)
{
    LPSPI_EDMA_HwDev_T *hwDevPtr = xferHandler->HwDevPtr;
    EDMA_TransferConfig_T edmaTransferConfigStruct = DRV_LPSPI_EDMA_CONFIG_DEFAULT_STRUCT;
    LPSPI_XferCommand_T lpspiXferCommandStruct = DRV_LPSPI_XFER_COMMAND_DEFAULT_STRUCT;

    /* 保存回调函数到xferHandler，每次传输均更新回调函数 */
    xferHandler->xferDoneCallbackFunc = xferDonecallback;

    /* 根据传参设定Rx DMA的搬运长度和目的地址 */
    DMAMUX_HAL_SetTriggerForDMA(hwDevPtr->EdmaLpspiRxChnIdx, eDMAMUX_TriggerSource_Disabled); /* 确保触发窗口是关闭的 */
    EDMA_HAL_ResetChannel(hwDevPtr->EdmaLpspiRxChnIdx);
    if (rxBufPtr) /* 接收有效数据 */
    {
        edmaTransferConfigStruct.DestAddr = (uint32_t)(rxBufPtr);
        edmaTransferConfigStruct.DestAddrIncPerTransfer = 1; /* 在接收队列内递增 */
    }
    else /* 保存到Dummy变量中，仅发送模式 */
    {
        edmaTransferConfigStruct.DestAddr = (uint32_t)(&(xferHandler->RxDummyData));
        edmaTransferConfigStruct.DestAddrIncPerTransfer = 0; /* 原地不动 */
    }
    edmaTransferConfigStruct.SrcAddrIncPerTransfer = 0; /* Rx时，DMA源地址不动 */
    edmaTransferConfigStruct.SrcAddr = (uint32_t)(&(hwDevPtr->LpspiBase->RDR));
    edmaTransferConfigStruct.MinorLoopCount = xferLen;
    edmaTransferConfigStruct.MinorLoopByteCount = 1U; /* 每次传输一个字节 */
    EDMA_HAL_ConfigTransfer(hwDevPtr->EdmaLpspiRxChnIdx, &edmaTransferConfigStruct);

    /* 启用接收完成中断 */
    EDMA_HAL_EnableInterruptOnMajorLoopDone(hwDevPtr->EdmaLpspiRxChnIdx, true); /* Rx搬运完成后发出中断信号 */

    /* 根据传参设定Tx DMA的搬运长度及源地址 */
    DMAMUX_HAL_SetTriggerForDMA(hwDevPtr->EdmaLpspiTxChnIdx, eDMAMUX_TriggerSource_Disabled); /* 确保触发窗口是关闭的 */
    EDMA_HAL_ResetChannel(hwDevPtr->EdmaLpspiTxChnIdx);
    if (txBufPtr) /* 发送用户数据，发送模式 */
    {
        edmaTransferConfigStruct.SrcAddr = (uint32_t)(txBufPtr);
        edmaTransferConfigStruct.SrcAddrIncPerTransfer = 1; /* 在发送队列内递增1字节 */

    }
    else /* 发送Dummy数据，仅接收模式 */
    {
        xferHandler->TxDummyData = 0xFF;
        edmaTransferConfigStruct.SrcAddr = (uint32_t)(&(xferHandler->TxDummyData));
        edmaTransferConfigStruct.SrcAddrIncPerTransfer = 0; /* 原地不动 */
    }
    edmaTransferConfigStruct.DestAddrIncPerTransfer = 0; /* Tx时，DMA目的地址不动 */
    edmaTransferConfigStruct.DestAddr = (uint32_t)(&(hwDevPtr->LpspiBase->TDR));
    /* edmaTransferConfigStruct.MinorLoopCount = xferLen; */
    EDMA_HAL_ConfigTransfer(hwDevPtr->EdmaLpspiTxChnIdx, &edmaTransferConfigStruct);

    NVIC_EnableIRQ(hwDevPtr->EdmaRxChnIRQn); /* 在NVIC中开启监听中断信号 */

    /* 首先写入发送配置命令字 */
    /* lpspiXferCommandStruct. */
    LPSPI_HAL_SetXferCommand(hwDevPtr->LpspiBase, &lpspiXferCommandStruct);

    /* 解除对两个DMA通道的锁定，启动发送过程 */
    EDMA_HAL_EnableHwTrigger(hwDevPtr->EdmaLpspiRxChnIdx, true);
    EDMA_HAL_SetHwTriggerAutoDisabledOnMajorLoopDone(hwDevPtr->EdmaLpspiRxChnIdx, true);
    DMAMUX_HAL_SetTriggerForDMA(hwDevPtr->EdmaLpspiRxChnIdx, hwDevPtr->EdmaLpspiRxTriggerSource);
    EDMA_HAL_EnableHwTrigger(hwDevPtr->EdmaLpspiTxChnIdx, true);
    EDMA_HAL_SetHwTriggerAutoDisabledOnMajorLoopDone(hwDevPtr->EdmaLpspiTxChnIdx, true);
    DMAMUX_HAL_SetTriggerForDMA(hwDevPtr->EdmaLpspiTxChnIdx, hwDevPtr->EdmaLpspiTxTriggerSource);
}

void LPSPI_EDMA_MasterXferIRQHandler(LPSPI_EDMA_MasterXferHandler_T *xferHandler)
{
    LPSPI_EDMA_HwDev_T *hwDevPtr = xferHandler->HwDevPtr;

    /* 停用接收完成中断 */
    EDMA_HAL_EnableInterruptOnMajorLoopDone(hwDevPtr->EdmaLpspiRxChnIdx, false);
    NVIC_DisableIRQ(hwDevPtr->EdmaRxChnIRQn);

    /* DMA触发窗口在搬运完成后自动关闭，此处不需要手动关闭窗口，但需要手动清除完成标志位 */
    EDMA_HAL_ClearMajorLoopDoneFlag(hwDevPtr->EdmaLpspiRxChnIdx);
    EDMA_HAL_ClearInterruptFlagOfMajorLoopDone(hwDevPtr->EdmaLpspiRxChnIdx);
    DMAMUX_HAL_SetTriggerForDMA(hwDevPtr->EdmaLpspiTxChnIdx, eDMAMUX_TriggerSource_Disabled);

    EDMA_HAL_ClearMajorLoopDoneFlag(hwDevPtr->EdmaLpspiTxChnIdx);
    EDMA_HAL_ClearInterruptFlagOfMajorLoopDone(hwDevPtr->EdmaLpspiTxChnIdx);
    DMAMUX_HAL_SetTriggerForDMA(hwDevPtr->EdmaLpspiRxChnIdx, eDMAMUX_TriggerSource_Disabled);

    if (xferHandler->xferDoneCallbackFunc)
    {
        (*(xferHandler->xferDoneCallbackFunc))();
        xferHandler->xferDoneCallbackFunc = NULL; /* 执行完毕后清零，防止下次传输误入 */
    }
}

/* EOF. */
